package cmd

import (
	"bbs125Check/RROcr"
	"bbs125Check/bbs125"
	"bbs125Check/config"
	"bbs125Check/cookieJar"
	"bbs125Check/initialize"
	"context"
	"errors"
	"fmt"
	"gitee.com/kaylee595/gkaylee"
	"github.com/samber/lo"
	"github.com/spf13/viper"
	"log/slog"
	"net/http"
	"net/url"
	"os"
	"time"

	"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
	Use:   "bbs125Check",
	Short: "精易论坛签到",
	Long:  `精易论坛签到`,
	Run: func(cmd *cobra.Command, args []string) {
		emotion, _ := cmd.Flags().GetString("emotion")
		emotions := []bbs125.Emotion{
			bbs125.EmotionHappy,
			bbs125.EmotionSad,
			bbs125.EmotionDepressed,
			bbs125.EmotionBored,
			bbs125.EmotionAngry,
			bbs125.EmotionWipingSweat,
			bbs125.EmotionStruggling,
			bbs125.EmotionLazy,
			bbs125.EmotionBad,
		}
		if !lo.Contains(emotions, bbs125.Emotion(emotion)) {
			slog.Error("噢不! 你给了我一个我不知道的签到情绪!", slog.String("emotion", emotion))
			slog.Warn("你可以使用--help标记查看能够使用的情绪")
			return
		}

		// 创建用于控制整个程序运行周期的Context
		// 整个程序的生命周期为什么是3分钟, 而不是更短? 这是因为签到如果遇到没有登录/登录失效的话, 会自动登录, 但是登录是需要进行滑块拼图验证码的
		// 这个滑块拼图验证是比较耗时的, 而且有时候需要多尝试几遍才可以成功, 但是最多只会滑3次, 所以你需要给足它一些时间进行验证码识别
		ctx, cancelFunc := context.WithTimeout(context.Background(), 3*time.Minute)
		defer cancelFunc()
		// 创建cookie管理器
		ckJar := cookieJar.NewJar()
		// 创建HTTP客户端
		httpClient := &http.Client{
			Jar: ckJar,
		}
		// 创建bbs125客户端
		bbs125Client := bbs125.NewClient(httpClient, "")

		// 加载本地cookies
		bbs125laCookiesFilename := gkaylee.Ternary(len(config.Cfg.Username) > 0,
			fmt.Sprintf("bbs.125.la.%s.cookies", config.Cfg.Username),
			"bbs.125.la.cookies",
		)
		cookieDomain := &url.URL{
			Scheme: "https",
			Host:   ".125.la",
		}
		err := ckJar.LoadFromFile(cookieDomain, bbs125laCookiesFilename)
		if err != nil {
			if !errors.Is(err, os.ErrNotExist) {
				slog.Warn("加载本地cookies出错", "err", err)
				return
			}
			// 本地不存在cookie文件, 肯定是无法签到的, 这里可以先登录一下
			if len(config.Cfg.Username) == 0 || len(config.Cfg.Password) == 0 || len(config.Cfg.RROcrAppkey) == 0 {
				slog.Info("签到是需要登录的, 如果你需要程序自动登录, 请配置完整的username\\password和rrocr_appkey, 否则请你手动更新cookies文件",
					slog.String("cookies文件名", bbs125laCookiesFilename),
				)
				return
			}
			// 有配置信息则进行登录咯
			slog.Info("正在登录中...")
			err = loginBBS125(ctx, bbs125Client, RROcr.NewClient(httpClient, config.Cfg.RROcrAppkey, RROcr.WithShareCode(rrOcrDevShareCode)))
			if err != nil {
				slog.Error("精易论坛登录失败", "err", err)
				return
			}
		}
		defer func() {
			// 程序退出时保存最新cookies
			err = ckJar.SaveToFile(cookieDomain, bbs125laCookiesFilename)
			if err != nil {
				slog.Warn("保存cookies到本地出错", "err", err)
			}
		}()

		// 签到
		checkResult, err := bbs125Client.Check(ctx, bbs125.Emotion(emotion))
		if err != nil {
			if errors.As(err, &checkResult) && checkResult.IsErrorTodayChecked() {
				slog.Info("精易论坛今天已经签到过了")
				return
			}
			if !errors.Is(err, bbs125.ErrNotLogin) {
				slog.Error("精易论坛签到失败", "err", err)
				return
			}
			// 没有登录, 检查一下是否有配置账号密码以极验识别的appkey
			if len(config.Cfg.Username) == 0 || len(config.Cfg.Password) == 0 || len(config.Cfg.RROcrAppkey) == 0 {
				slog.Info("页面提示需要登录, 可能是无效cookies, 如果你需要程序自动登录, 请配置完整的username\\password和rrocr_appkey, 否则请你手动更新cookies文件",
					slog.String("cookies文件名", bbs125laCookiesFilename),
				)
				return
			}
			// 有配置信息则进行登录咯
			slog.Info("页面提示需要登录, 可能是无效cookies, 正在登录中...")
			err = loginBBS125(ctx, bbs125Client, RROcr.NewClient(httpClient, config.Cfg.RROcrAppkey, RROcr.WithShareCode(rrOcrDevShareCode)))
			if err != nil {
				slog.Error("精易论坛登录失败", "err", err)
				return
			}
			// 重新签到
			checkResult, err = bbs125Client.Check(ctx, bbs125.Emotion(emotion))
			if err != nil {
				if errors.As(err, &checkResult) && checkResult.IsErrorTodayChecked() {
					slog.Info("精易论坛今天已经签到过了")
				} else {
					slog.Error("精易论坛签到失败", "err", err)
				}
				return
			}
		}
		slog.Info("精易论坛签到成功", "本次获得精币数量", checkResult.Data.Credit, "连续签到天数", checkResult.Data.MDays)
	},
}

func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func init() {
	// init
	cobra.OnInitialize(func() {
		configFilename, _ := rootCmd.PersistentFlags().GetString("config")
		_ = viper.BindPFlags(rootCmd.Flags())
		initialize.InitConfig(configFilename)
		initialize.InitOther()
	})

	// Here you will define your flags and configuration settings.
	// Cobra supports persistent flags, which, if defined here,
	// will be global for your application.
	rootCmd.PersistentFlags().String("config", "config.yaml", "配置文件路径")

	// Cobra also supports local flags, which will only run
	// when this action is called directly.
	rootCmd.Flags().StringP("username", "u", "", "精易论坛用户名")
	rootCmd.Flags().StringP("password", "p", "", "精易论坛密码")
	rootCmd.Flags().StringP("emotion", "e", "kx", "kx(开心);ng(难过);ym(郁闷);wl(无聊);nu(怒);ch(擦汗);fd(奋斗);yl(慵懒);shuai(衰)")
}
